package org.kernely.ui;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.kernely.core.service.KernelyBundleService;
import org.kernely.core.service.KernelySettingsService;
import org.kernely.persistence.service.SessionService;
import org.kernely.security.service.KernelySecurityService;
import org.kernely.ui.login.KernelyLoginEvent;
import org.kernely.ui.login.KernelyLoginPresenter;
import org.kernely.ui.login.KernelyLoginView;
import org.kernely.ui.mvp.IKernelyView;
import org.kernely.ui.mvp.KernelyEvent;
import org.kernely.ui.mvp.KernelyEventBus;
import org.kernely.ui.mvp.KernelyEventBusGeneral;
import org.kernely.ui.mvp.KernelyEventReceiver;
import org.kernely.ui.mvp.KernelyEventSender;
import org.kernely.ui.mvp.KernelyPresenter;
import org.kernely.ui.services.KernelyUIBundle;
import org.kernely.ui.services.KernelyUIBundleService;
import org.kernely.ui.services.KernelyUIBundleServiceListener;

import com.vaadin.Application;
import com.vaadin.service.ApplicationContext.TransactionListener;
import com.vaadin.terminal.Sizeable;
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
import com.vaadin.ui.Panel;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.themes.Reindeer;

/**
 * MainApplication class
 */
public class MainApplication extends Application implements KernelyUIBundleServiceListener, KernelyEventReceiver, KernelyEventSender {

	private static final String BUNDLE_NAME = "KERNELY_MAIN_UI";
	private static final String DBKEY_HOMEPAGE_CLASS = "homepage.class";
	private static final String DBKEY_HOMEPAGE_BUNDLE = "homepage.bundle";
	
	/**
	 * SessionService service
	 */
	private static SessionService service;
	
	/**
	 * Sets the session service.
	 * 
	 * @param service
	 *            the session service to set
	 */
	public final void setPersistenceService(SessionService serv) {
		service = serv;
	}
	
	/**
	 * Kernely declarative services
	 */
	private static KernelyBundleService coreService;
	private static KernelySecurityService securityService;
	private static KernelyUIBundleService uiBundleService;
	private static KernelySettingsService settingsService;

	// Vaadin objects
	private Window window;
	private VerticalLayout mainLayout;
	private IKernelyView loginView;
	private IKernelyView mainView;
	private KernelyPresenter loginPresenter;
	private KernelyPresenter mainPresenter;

	private static KernelyEventBus eventBusMA;

	/**
	 * The serialization id.
	 */
	private static final long serialVersionUID = 1342675030168502215L;

	/**
	 * MainApplication's Servlet
	 */
	public static class VaadinServlet extends AbstractApplicationServlet {

		/**
		 * The Serial Version ID
		 */
		private static final long serialVersionUID = 102610594159015L;

		/**
		 * Retrieve the application's class associated to this servlet
		 */
		@SuppressWarnings("unchecked")
		@Override
		protected final Class getApplicationClass() {
			return MainApplication.class;
		}

		/**
		 * Retrieve a new instance of the application associated to this servlet
		 */
		@Override
		protected final Application getNewApplication(HttpServletRequest request) throws ServletException {
			return new MainApplication();
		}

	}

	/**
	 * Get the coreService
	 * @return the core service
	 */
	public static KernelyBundleService getCoreService() {
		return coreService;
	}

	/**
	 * Set the coreService
	 * 
	 * @param coreService
	 *            : the KernelyBundleService service
	 */
	public static void setCoreService(KernelyBundleService coreService) {
		if (MainApplication.coreService == null) {
			MainApplication.coreService = coreService;
		}
	}

	/**
	 * This function initializes the main window of Kernely.
	 */
	@SuppressWarnings("serial")
	@Override
	public final void init() {
		uiBundleService.addListener(this);

		// We register to the event bus to be fired by events
		if(SecurityUtils.getSecurityManager() != null){

			Session session = (Session) SecurityUtils.getSubject().getSession();
			KernelyEventBus eventBus = (KernelyEventBus) session.getAttribute("eventBus");
			if (eventBus == null) {
				eventBus = new KernelyEventBus();
				session.setAttribute("eventBus", eventBus);
			}
			eventBus.addListener(this);

			eventBusMA = eventBus;
			
			// We instantiate Views and Presenters for later.
			loginView = new KernelyLoginView();
			loginPresenter = new KernelyLoginPresenter();

			mainView = new MainApplicationView();
			mainPresenter = new MainApplicationPresenter();

			window = new Window("Kernely: Portal");
			window.setHeight(100, Sizeable.UNITS_PERCENTAGE);
			window.setWidth(100,Sizeable.UNITS_PERCENTAGE);

			Panel windowPanel = new Panel();
			windowPanel.setSizeFull();
			windowPanel.setScrollable(true);
			windowPanel.setStyleName(Reindeer.PANEL_LIGHT);
			mainLayout = (VerticalLayout)windowPanel.getContent();
			mainLayout.setWidth(100, Sizeable.UNITS_PERCENTAGE);
			mainLayout.setMargin(false);
			mainLayout.setSpacing(false);

			window.setContent(windowPanel);

			// Set the immediate display mode
			window.setImmediate(true);
			setTheme("kernelytheme");
			createMainWindow();
		}
		else{
			System.out.println("Security manager is null");
		}
		
		
		this.getContext().addTransactionListener(new TransactionListener(){
			/**
			 * @see com.vaadin.service.ApplicationContext.TransactionListener#transactionEnd(com.vaadin.Application, java.lang.Object)
			 */
			public void transactionEnd(Application application,
					Object transactionData) {
				if (application == MainApplication.this) {
					closeSession();
				}
				
			}
			/**
			 * @see com.vaadin.service.ApplicationContext.TransactionListener#transactionStart(com.vaadin.Application, java.lang.Object)
			 */
			public void transactionStart(Application application,
					Object transactionData) {
				// Nothing to do.
			}
			
		});
	}
	
	/**
	 * Close hibernate current session.
	 */
	private void closeSession() {
		if(service.getSessionFactory().getCurrentSession().isOpen()){
			org.hibernate.Session sess = service.getSessionFactory().getCurrentSession();
			if(sess.getTransaction().isActive()) {
				sess.flush();
				sess.getTransaction().commit();
			}
			if(sess.isOpen()){
				sess.close();
			}
		}
	}

	/**
	 * Create the main window of the application
	 */
	public final void createMainWindow() {
		// We retrieve the current user and check if he's authenticated or not.
		Subject user = SecurityUtils.getSubject();
		if (user.isAuthenticated()) {
			showApplicationPage();
		} else {
			showLoginPage();
		}
		// Define the window as main window
		setMainWindow(window);
	}

	/**
	 * Create the login page's view
	 */
	public final void showLoginPage() {
		// Association View - Presenter
		loginPresenter.setView(loginView);
		VerticalLayout view = (VerticalLayout) loginPresenter.getView();
		mainLayout.addComponent(view);
	}

	/**
	 * Create the application page's view
	 */
	public final void showApplicationPage() {
		// Association View - Presenter
		mainPresenter.setView(mainView);
		mainLayout.replaceComponent((VerticalLayout) loginView, (VerticalLayout) mainView);
	}

	/**
	 * Called when a KernelyViewEvent is fired.
	 */
	public final void handleKernelyEvent(KernelyEvent event) {
		if (event instanceof KernelyLoginEvent) {
			createMainWindow();
		}
	}

	/**
	 * Gets the Kernely event bus.
	 * 
	 * @return the eventBus
	 */
	public static KernelyEventBus getEventBus() {
		Session session = (Session) SecurityUtils.getSubject().getSession();
		return (KernelyEventBus) session.getAttribute("eventBus");
	}

	/**
	 * Sets the KernelyUIBundleService Declarative Service.
	 * 
	 * @param uiBundleService
	 *            : the KernelyUIBundleService service
	 */
	public final void setUIBundleService(KernelyUIBundleService uiBundleService) {
		MainApplication.uiBundleService = uiBundleService;
	}

	/**
	 * Unsets the KernelyUIBundleService Declarative Service.
	 * 
	 * @param uiBundleService
	 *            : the KernelyUIBundleService service
	 */
	public final void unsetUIBundleService() {
	}


	/**
	 * Sets the security Declarative Service
	 * @param service The security service.
	 */
	public final void setSecurityService(KernelySecurityService service) {
		securityService = service;
	}

	/**
	 * Unsets the security Declarative Service
	 * @param service The security service.
	 */
	public final void unsetSecurityService(KernelySecurityService service) {}

	/**
	 * Gets the security service.
	 * @return the security service.
	 */
	public final static KernelySecurityService getSecurityService() {
		return securityService;
	}


	/**
	 * Get the UI bundle service.
	 * @return ui bundle service
	 */
	public final static KernelyUIBundleService getKernelyUIBundleService() {
		return uiBundleService;
	}

	/**
	 * Called by the Service Component Runtime when the declarative service is
	 * started
	 * 
	 * @param kbs
	 *            : the kernely bundle service (core)
	 */
	public final void setModuleService(KernelyBundleService kbs) {
	}

	/**
	 * Called by the Service Component Runtime when the declarative service is
	 * stopped
	 * 
	 * @param kbs
	 *            : the kernely bundle service (core)
	 */
	public final void unsetModuleService(KernelyBundleService kbs) {
	}
	
	/**
	 * Called by the Service Component Runtime when the declarative service is
	 * started
	 * 
	 * @param settingsService
	 *            : the settings service (core)
	 */
	public final void setSettingsService(KernelySettingsService service) {
		settingsService = service;
	}

	/**
	 * Called by the Service Component Runtime when the declarative service is
	 * started
	 * 
	 * @return settingsService
	 *            : the settings service (core)
	 */
	public final static KernelySettingsService getSettingsService() {
		return settingsService;
	}

	
	/**
	 * Called by the Service Component Runtime when the declarative service is
	 * stopped
	 * 
	 */
	public final void unsetSettingsService() {
	}

	/**
	 * Called when a new UI bundle is detected
	 */
	@Override
	public final void moduleRegistered(KernelyUIBundleService source, KernelyUIBundle module) {

		// We warn the general event bus that something changed in ui modules.
		KernelyEventBusGeneral.getInstance().broadcastEvent(new KernelyUIBundleDetectionEvent(this));
	}

	/**
	 * Called when an UI bundle is removed
	 */
	@Override
	public final void moduleUnregistered(KernelyUIBundleService source, KernelyUIBundle module) {

		// We warn the general event bus that something changed in ui modules.
		KernelyEventBusGeneral.getInstance().broadcastEvent(new KernelyUIBundleDetectionEvent(this));
	}
	
	

	/**
	 * Gets the bundleName
	 * @return the bundleName
	 */
	public static String getBundleName() {
		return BUNDLE_NAME;
	}

	/**
	 * Gets the dbkeyHomepageClass, used in settings.
	 * @return the dbkeyHomepageClass
	 */
	public static String getDbkeyHomepageClass() {
		return DBKEY_HOMEPAGE_CLASS;
	}

	/**
	 * Gets the dbkeyHomepageBundle, used in settings.
	 * @return the dbkeyHomepageBundle
	 */
	public static String getDbkeyHomepageBundle() {
		return DBKEY_HOMEPAGE_BUNDLE;
	}

	
	
}